#include "Common/TestSystem.h"

namespace Nezha
{

	class SampleCollectionImpl : public NZTestSampleCollector::SampleCollection
	{
	public:
		typedef PArray<NZTestSample> TestSampleArray;

		bool mFinished;
		u32 mFrameStartTicks;
		u32 mCounter;				// counter in NZTestSample structure.
		u32 mMaxSampleNum;
		NZTestSample mCurrentSample;
		TestSampleArray mSamples;

		void reset(u32 curTicks)
		{
			mCurrentSample.ErrorCode = 0;
			mCurrentSample.DetailInfoId = 0;
			mFrameStartTicks = curTicks;
			mCurrentSample.Counter = mCounter;
		}
	};

	NZTestSampleCollector::NZTestSampleCollector()
		:mParent(NULL)
		,mType(TST_NULL_SAMPLER)
	{
		mCollection = NZ_New SampleCollectionImpl();
	}

	NZTestSampleCollector::~NZTestSampleCollector()
	{
		SampleCollectionImpl* impl = static_cast<SampleCollectionImpl*>(mCollection);
		NEZHA_DELETE(impl);
	}

	void NZTestSampleCollector::startCollect(TestSampleType tst, NZTest* test, u32 maxSampleNum, u32 counter)
	{
		SampleCollectionImpl* impl = static_cast<SampleCollectionImpl*>(mCollection);
		impl->mSamples.clear();
		impl->mCounter = counter;
		impl->mMaxSampleNum = maxSampleNum;
		impl->reset(GetTimeMillisecond());
		impl->mFinished = false;
		mParent = test;
		mType = tst;
	}

	void NZTestSampleCollector::click()
	{
		SampleCollectionImpl* impl = static_cast<SampleCollectionImpl*>(mCollection);

		if(impl->mFinished || !mParent)
		{
			return;
		}

		u32 curTicks = GetTimeMillisecond();

		switch(mType)
		{
		case TST_FRAME_SAMPLER:
			{
				impl->mCurrentSample.Ticks = curTicks - impl->mFrameStartTicks;
				impl->mFrameStartTicks = curTicks;
				impl->mCurrentSample.ErrorCode = mParent->_queryStatus(impl->mCurrentSample.DetailInfoId);	
				impl->mSamples.push_back(impl->mCurrentSample);
				impl->reset(curTicks);
			}
			break;
		case TST_PEROID_SAMPLER:
			{
				u32 ticks = curTicks - impl->mFrameStartTicks;
				if(ticks >= impl->mCurrentSample.Counter)
				{
					impl->mCurrentSample.ErrorCode = mParent->_queryStatus(impl->mCurrentSample.DetailInfoId);
					impl->mSamples.push_back(impl->mCurrentSample);
					impl->reset(curTicks);
				}
			}
			break;
		case TST_COUNTED_SAMPLER:
			{
				u32 ticks = curTicks - impl->mFrameStartTicks;
				if(--impl->mCurrentSample.Counter == 0)
				{
					impl->mCurrentSample.ErrorCode = mParent->_queryStatus(impl->mCurrentSample.DetailInfoId);
					impl->mSamples.push_back(impl->mCurrentSample);
					impl->reset(curTicks);
				}
			}
			break;
		}

		if(impl->mSamples.size() >= impl->mMaxSampleNum)
		{
			impl->mFinished = true;
			mParent->_notifySampleCollectFinished();
		}
	}

	void NZTestSampleCollector::endCollect()
	{
		SampleCollectionImpl* impl = static_cast<SampleCollectionImpl*>(mCollection);

		if(!impl->mFinished)
		{
			if(mParent)
				mParent->_notifySampleCollectFinished();

			impl->mFinished = false;
		}
	}

	void NZTestSampleCollector::clear()
	{
		(static_cast<SampleCollectionImpl*>(mCollection))->mSamples.clear();
	}

	u32 NZTestSampleCollector::getNumSamples() const
	{
		return (static_cast<SampleCollectionImpl*>(mCollection))->mSamples.size();
	}

	const NZTestSample& NZTestSampleCollector::getSample(u32 i) const
	{
		NZ_ASSERT(i < (static_cast<SampleCollectionImpl*>(mCollection))->mSamples.size());

		return (static_cast<SampleCollectionImpl*>(mCollection))->mSamples[i];
	}

	bool NZTestSampleCollector::getSampleCollectFinised() const
	{
		return (static_cast<SampleCollectionImpl*>(mCollection))->mFinished;
	}

	void NZTestSampleCollector::reportAllSamples()
	{
		SampleCollectionImpl* impl = static_cast<SampleCollectionImpl*>(mCollection);

		int cnt = 0;
		for(PArray<NZTestSample>::iterator it = impl->mSamples.begin(); it != impl->mSamples.end(); it++)
		{
			printf("Sample %d cost time %d ms\n", cnt++, (*it).Ticks);
		}
	}



	bool NZTest::update(float dt)
	{
		if(mSampler)
		{
			if(mSampler->getSampleCollectFinised())
			{
				return true;
			}
		}

		bool b = updateImpl(dt);

		if(mSampler)
		{
			mSampler->click();
		}

		return b;
	}

	void NZTest::setSampleCollector(bool b)
	{
		if(b)
		{
			if(!mSampler)
				mSampler = TestSystem::getSingleton().getDefaultSampler();
		}
		else
		{
			if(mSampler)
			{
				mSampler = NULL;
			}
		}
	}

	bool NZTest::initialize()
	{
		if(mSampleType != TST_NULL_SAMPLER)
		{
			setSampleCollector(true);
		}

		if(mSampler)
		{
			mSampler->startCollect(mSampleType, this, mSampleCounter, mTimeCounter);
		}

		return _initialize();
	}

	bool NZTest::reset()
	{
		if(--mTestCounter == 0)
		{
			return false;
		}

		if(_reset())
		{
			if(mSampler)
			{
				mSampler->startCollect(mSampleType, this, mSampleCounter, mTimeCounter);
			}
		}

		return false;
	}

	void NZTest::uninitialize()
	{
		reportAllSample();

		_uninitialize();
	}

	void NZTest::_notifySampleCollectFinished()
	{

	}

	void NZTest::reportAllSample()
	{
		if(mSampler)
		{
			mSampler->reportAllSamples();
		}
	}



	NEZHA_SINGLETON_IMPLEMENT(TestSystem)

	void TestSystem::initializeFromCfg(const _string& fileName)
	{
		// TODO
	}

	void TestSystem::registerFacoty(TestFactory* fac)
	{
		if(!fac)
		{
			return;
		}

		PMap<_string, TestFactoryPtr>::iterator it = mFacoties.find(fac->getName());
		if(it == mFacoties.end())
		{
			TestFactoryPtr ptr(fac);
			mFacoties.insert(std::pair<_string, TestFactoryPtr>(fac->getName(), ptr));
		}
	}

	void TestSystem::addTest(const _string& testName, u32 testTimes /* = 1 */)
	{
		PMap<_string, TestFactoryPtr>::iterator it = mFacoties.find(testName);
		if(it != mFacoties.end())
		{
			addTest(it->second->create(), testTimes);
		}
	}

	void TestSystem::addTest(NZTest* t, u32 times)
	{
		if(!t || t->isTesting() || times == 0)
		{
			return;
		}

		NZTestPtr ptr(t);
		t->mTestCounter = times;
		mWaitingQueue.push_back(ptr);
	}

	void TestSystem::update(float dt)
	{
		if(!mCurrentTest)
		{
			NZTest* prev = NULL;
			for(PArray<NZTestPtr>::iterator it = mWaitingQueue.begin(); it != mWaitingQueue.end(); it++)
			{
				if(prev)
				{
					prev->mNext = (*it);
				}
				else
				{
					mCurrentTest = (*it);
				}

				prev = (*it).get();
			}

			mWaitingQueue.clear();

			if(mCurrentTest.notNull())
			{
				while(!mCurrentTest->initialize())
				{
					mCurrentTest->uninitialize();

					NZTestPtr next = mCurrentTest->mNext;
					mCurrentTest = next;
				}

				mCurrentTest->mTesting = true;
			}
		}
		else
		{
			if(mCurrentTest->update(dt))
			{
				if(!mCurrentTest->reset())
				{
					mCurrentTest->mTesting = false;
					mCurrentTest->uninitialize();

					NZTestPtr next = mCurrentTest->mNext;
					mCurrentTest = next;

					if(mCurrentTest.notNull())
					{
						while(!mCurrentTest->initialize())
						{
							mCurrentTest->uninitialize();
							mCurrentTest = mCurrentTest->mNext;
						}

						mCurrentTest->mTesting = true;
					}
				}
			}
		}
	}

	void TestSystem::randomizeEnglishString(_string& str, u16 maxLen)
	{
		if(maxLen < 3)
			maxLen = 3;

		u16 _len = 3;
		if(maxLen > 3)
			_len += NZRand() % (maxLen - 3);

		str.clear();
		str.resize(_len + 1, 0);

		// 48 - 57
		// 65 - 90
		// 97 - 122
		for(u16 i = 0; i < _len; i++)
		{
			int n = NZRand() % 3;
			if(n == 0)
			{
				str[i] = (char)NZRandom::Rangei(48, 58);
			}
			else if(n == 1)
			{
				str[i] = (char)NZRandom::Rangei(65, 91);
			}
			else
			{
				str[i] = (char)NZRandom::Rangei(97, 123);
			}
		}

		str[_len] = 0;
	}
}